//
// Created by wujehy on 2020/6/24.
//

#include <iostream>
#include <memory>
/**
 * @brief 指针指针 是 c++ 11 以后的新特征 , 主要是解决指针 内存泄漏的问题
 * 智能指针内部都有一个计数器 会计算使用了几次 , 直到 都没有人使用这个指针的时候 自动销毁
 * 主要 讲最常用的 两组 unique_ptr (只有一个有效) , shared_ptr( 多个对象指向一个)
 * unique_ptr 该对象传递的时候 , 会将指针传递给新的对象 , 旧的对象就不能用这个指针了 (类似rust 的所有权转移)
 * shared_ptr 改对象传递的时候 指针计数会加1 ,所有对象传递对象指向同一个指针 ,用完shared_ptr对象 , 指针计数减1 0 的时候自动销毁
 *
 * 具体看案例 基本用法
 * @return
 */

/**
 * @brief 构造器和析构器 用于观察 对象 创建 和 销毁的状态
 */
class TestDataType
{
public:
    TestDataType(int id, const std::string &name) : id( id ), name( name )
    {
        std::cout << " init TestDataType id = " << this->id << " name = " << this->name << std::endl;
    }

    virtual ~TestDataType()
    {
        std::cout << " delete TestDataType id = " << this->id << " name = " << this->name << std::endl;

    }

    int getId() const
    {
        return id;
    }

    const std::string &getName() const
    {
        return name;
    }

private:
    int id;
    std::string name;
};

int main()
{
//std::unique_ptr()
    std::cout << " unique_ptr Test Start " << std::endl;
// 创建 智能指针对象 , 对象 内部保存着 一个 具体对象 的指针 , 会自动释放
    std::unique_ptr<TestDataType> port_obj1 = std::unique_ptr<TestDataType>( new TestDataType( 1, "test1" ));
// 获取对象指针 , 内有new 所以对象实体在  port_obj1 里面
    TestDataType *obj1 = port_obj1.get();
    // 访问 方式１　
    std::cout << " obj1 addr = 0x" << std::hex << obj1 << " , id = " << obj1->getId() << " name  = " << obj1->getName()
              << std::endl;

//    或者直接智能指针访问
    std::cout << " port_obj1 , id = " << port_obj1->getId() << " name  = " << port_obj1->getName() << std::endl;

//  传递指针 , unique_ptr 只能有一个掌有所有权　所以不能拷贝只能转移
    std::unique_ptr<TestDataType> port_obj2 = std::move( port_obj1 );

//    或者直接智能指针访问
    std::cout << " port_obj2 , id = " << port_obj2->getId() << " name  = " << port_obj2->getName() << std::endl;
//    obj1 的地址还是那个地址
    std::cout << " obj1 addr = 0x" << std::hex << obj1 << " , id = " << obj1->getId() << " name  = " << obj1->getName()
              << std::endl;
//　但是　port_obj1　已经不能存储了　指针没了
    if (port_obj1.get() == nullptr)
    {
        //　port_obj1　对象内部没有指针了, 但是没有析构函数触发　表明　内存空间还是生效的　obj1　是具体的地址　能够正常访问
        std::cout << " port_obj1 is nullptr " << std::endl;
    }

//  当然　你可等到　程序结束自动释放　，或者　传递空指针　手动释放具体对象　
    port_obj2.reset( nullptr );

    std::cout << " unique_ptr Test End " << std::endl;

    /**
     * 可以看到　init　一次　和　delete　一次　，
     * 如果　reset　去掉　则会　unique_ptr Test End　之后　释放内存
     * output :　
     *  unique_ptr Test Start
     *  init TestDataType id = 1 name = test1
     *  obj1 addr = 0x0x5640ff92c2c0 , id = 1 name  = test1
     *  port_obj1 , id = 1 name  = test1
     *  port_obj2 , id = 1 name  = test1
     *  obj1 addr = 0x0x5640ff92c2c0 , id = 1 name  = test1
     *  port_obj1 is nullptr
     *  delete TestDataType id = 1 name = test1
     *  unique_ptr Test End
     */

    std::cout <<std::endl<<std::endl<< " shared_ptr Test start " << std::endl;

    std::shared_ptr<TestDataType> port_obj3 = std::shared_ptr<TestDataType>(new TestDataType(3, "test3"));

    std::cout << " port_obj3 , id = " << port_obj3->getId() << " name  = " << port_obj3->getName() <<" count =" << port_obj3.use_count() << std::endl;
    // 前面的用法一样　，　不同的是对象可以拷贝
    std::shared_ptr<TestDataType> port_obj4 =port_obj3;
    std::cout << " port_obj4 , id = " << port_obj4->getId() << " name  = " << port_obj4->getName() <<" count =" << port_obj4.use_count() << std::endl;
    std::shared_ptr<TestDataType> port_obj5 =port_obj3;
    std::cout << " port_obj5 , id = " << port_obj5->getId() << " name  = " << port_obj5->getName() <<" count =" << port_obj5.use_count() << std::endl;

//   也可以转移
    std::cout <<" doing move ... " <<std::endl;
    std::shared_ptr<TestDataType> port_obj6 =std::move(port_obj3);
    if(port_obj3.get()== nullptr)
    {
        std::cout <<" port_obj3 is nullptr" <<std::endl;
    }
    std::cout << " port_obj4 , id = " << port_obj4->getId() << " name  = " << port_obj4->getName()<<" count =" << port_obj4.use_count()  << std::endl;
    std::cout << " port_obj5 , id = " << port_obj5->getId() << " name  = " << port_obj5->getName()<<" count =" << port_obj5.use_count()  << std::endl;
    std::cout << " port_obj6 , id = " << port_obj6->getId() << " name  = " << port_obj6->getName()<<" count =" << port_obj6.use_count() << std::endl;

    std::cout << " shared_ptr Test end " << std::endl;

    /**
     * @brief　可以看到　shared_ptr　可以在多个智能指针对象中共享同一个内存地址
     * 析构也析构一次，　更重要的是　内有执行　delete 操作　在程序执行结束后自动　触发了析构函数
     * output :
     * shared_ptr Test start
     * init TestDataType id = 3 name = test3
     * port_obj3 , id = 3 name  = test3
     * port_obj4 , id = 3 name  = test3
     * port_obj5 , id = 3 name  = test3
     * doing move ...
     * port_obj3 is nullptr
     * port_obj4 , id = 3 name  = test3
     * port_obj5 , id = 3 name  = test3
     * port_obj6 , id = 3 name  = test3
     * shared_ptr Test end
     * delete TestDataType id = 3 name = test3
     */
    return 0;
}